package cn.iotnc.camera.ui.main

import android.app.Application
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import cn.iotnc.camera.BuildConfig
import cn.iotnc.camera.base.BaseViewModel
import cn.iotnc.camera.model.BaseResponse
import cn.iotnc.camera.model.CameraBean
import cn.iotnc.camera.model.IotncThrowable.Companion.SUCCESS
import cn.iotnc.camera.model.SearchBody
import cn.iotnc.camera.util.*
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
import org.jetbrains.anko.info
import org.jetbrains.anko.warn
import java.net.SocketException
import javax.inject.Inject

@HiltViewModel
class MainViewModel @Inject constructor(app: Application) : BaseViewModel(app) {

    val cameraDeviceLiveData = MutableLiveData<ArrayList<CameraBean>>()
    val searchStateLiveData = MutableLiveData<Boolean>()

    fun addCamera(bean: CameraBean) {
        val list: ArrayList<CameraBean> = cameraDeviceLiveData.value ?: ArrayList()
        if (list.indexOfFirst { it.ip == bean.ip } == -1) {
            list.add(bean)
            list.sortBy { it.ip.replace(".", "").toLong() }
        }
        cameraDeviceLiveData.postValue(list)
    }

    fun startSearch() = viewModelScope.launch(Dispatchers.IO) {
        searchStateLiveData.postValue(true)
        val list = ArrayList<CameraBean>()
        val socket = PORT.getMulticastSocket()
        info { "startSearch: init socket=$socket, my ip=${app.ip()}" }

        val receive = async {
            info { "startSearch: receiveMessage E..." }
            while (isActive) {
                try {
                    val message = socket.receiveMsg()
                    //debug版本调试，返回一条模拟消息
                    if (BuildConfig.DEBUG
                        && message.content.contains("magic".toRegex())
                        && message.content.contains("20200101".toRegex())
                    ) {
                        warn { "startSearch: DEBUG and send msg......." }
                        val testMessage: String = Gson().toJson(
                            BaseResponse(
                                0, 0, "", CameraBean(app.ip(), "","1.9.8", "jpg")
                            )
                        )
                        socket.sendMessage(Message(testMessage, message.ip))
                        continue
                    }

                    //解析返回的消息
                    val typeToken: TypeToken<BaseResponse<CameraBean>> =
                        object : TypeToken<BaseResponse<CameraBean>>() {}
                    val response =
                        Gson().fromJson(message.content, typeToken.type) as BaseResponse<CameraBean>
                    if (response.code == SUCCESS && response.data != null && list.indexOfFirst { it.ip == response.data.ip } == -1) {
                        list.add(response.data)
                    }
                } catch (e: Exception) {
                    warn { "startSearch: e = $e" }
                    //we have closed the socket
                    if (e is SocketException && e.message == "Socket closed") break
                }
            }
            info { "startSearch: receiveMessage X..." }
        }

        val send = async {
            repeat(6) {
                delay(500L)
                try {
                    val message = Message(
                        Gson().toJson(
                            SearchBody()
                        )
                    )
                    warn { "startSearch: sendMessage=$message" }
                    socket.sendMessage(message)
                } catch (e: Exception) {
                    warn { "startSearch sendMessage error=$e" }
                }
                delay(500L)
            }
            true
        }
        if (send.await()) {
            socket.close()
            receive.cancelAndJoin()
        }
        list.sortBy {
            it.ip.replace(".", "").toLong()
        }
        cameraDeviceLiveData.postValue(list)
        searchStateLiveData.postValue(false)
    }
}